﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace DarwinIsDead {
    /// <summary>
    /// Represents an environment for entities to operate.
    /// </summary>
    public class World {
        private List<Entity> entities;
        private List<Entity> drawList;
        private SpriteBatch spriteBatch;
        private SpriteFont debugFont;

        /// <summary>
        /// Reference to game object
        /// </summary>
        public Game Game { get; private set; }

        /// <summary>
        /// Reference to camera
        /// </summary>
        public Camera Camera { get; private set; }

        /// <summary>
        /// Getter and Setter for worlds backgorund object
        /// We need access to background because it contains getZombieSpawn
        /// and getPlayerSpawn. These methods allow us access the spawn points.
        /// </summary>
        public Background Background { get; set; }

        public AudioListener AudioListener { get; set; }

        /// <summary>
        /// Constructs a new World with a reference to the game object
        /// </summary>
        /// <param name="game">Reference to game object</param>
        public World(Game game) {
            Game = game;
            Camera = new Camera();
            AudioListener = new AudioListener();

            entities = new List<Entity>();
            drawList = new List<Entity>();
            spriteBatch = new SpriteBatch(game.GraphicsDevice);

            debugFont = game.Content.Load<SpriteFont>("Fonts\\Courier New");
            

            Background = new Background(this, spriteBatch);
        }

        /// <summary>
        /// Add an entity to the world
        /// </summary>
        /// <param name="entity">Entity to add</param>
        public void AddEntity(Entity entity) {
            entities.Add(entity);
            drawList.Add(entity);
        }

        /// <summary>
        /// Remove an entity from the world
        /// </summary>
        /// <param name="entity">Reference to entity to remove  </param>
        public void RemoveEntity(Entity entity) {
            entity.Destroy = true;
        }

        /// <summary>
        /// Retrieves a list of Entities from the world by type.
        /// </summary>
        public List<Entity> GetEntities<T>() {
            List<Entity> ret = new List<Entity>();
            foreach (Entity entity in entities) {
                if (entity is T) {
                    ret.Add(entity);
                }
            }
            return ret;
        }

        public void Update(GameTime gameTime) {
            for (int i = 0; i < entities.Count; i++) {
                Entity entity = entities[i];
                if (entity == null || entity.Destroy) {
                    // Remove any entity marked for removal
                    entities.Remove(entity);
                    drawList.Remove(entity);
                    i--;
                } else {
                    // Update entities
                    entity.Update(gameTime);
                }
            }

            AudioListener.Position = new Vector3(Camera.Position.X,
                Camera.Position.Y, 0f);
        }

        private int EntityCompare(Entity e1, Entity e2) {
            return (int)(e1.Depth - e2.Depth + 0.5f);
        }

        /// <summary>
        /// Renders entities to the screen
        /// </summary>
        public void Draw() {
            // Sort entities
            drawList.Sort(EntityCompare);


            //PresentationParameters presentation = new PresentationParameters();
            //presentation.BackBufferHeight = (int)(Math.Round((double)game.GraphicsDevice.Viewport.Height) * camera.Zoom);
            //presentation.BackBufferWidth = (int)(Math.Round((double)game.GraphicsDevice.Viewport.Width) * camera.Zoom);
            //presentation.DeviceWindowHandle = game.Window.Handle;
            //game.GraphicsDevice.Reset(presentation);
//            game.GraphicsDevice.SamplerStates[0].Filter = Microsoft.Xna.Framework.Graphics.TextureFilter.Point; 

            SamplerState sampler = new SamplerState();
            sampler.Filter = Microsoft.Xna.Framework.Graphics.TextureFilter.Point; 

            // Draw entities relative to camera
            spriteBatch.Begin(SpriteSortMode.Immediate,
                    BlendState.AlphaBlend, sampler, null, null, null,
                    Camera.Transformation(Game.GraphicsDevice));

            Background.Draw(spriteBatch);

            foreach (Entity entity in drawList) {
                entity.Draw(spriteBatch);
            }
            spriteBatch.End();

            // Draw entities relative to screen
            spriteBatch.Begin();
            foreach (Entity entity in entities) {
                entity.DrawUI(spriteBatch);
            
            }
            spriteBatch.End();
        }

        /// <summary>
        /// Checks collisions between entities
        /// </summary>
        /// <remarks>
        /// A call to <see cref="CheckCollisions"/> checks intersections
        /// between the given entity and every other entity in the World.
        /// When a collision occurs, each Entity's
        /// <see cref="Entity.Collision"/> method is called, and given
        /// information about the opposite Entity.
        /// </remarks>
        /// <param name="entity">Entity to check</param>
        /// <param name="others">List of colliding entities</param>
        /// <returns></returns>
        public bool CheckCollisions(Entity entity, List<Entity> others = null) {
            bool ret = false;
            Rectangle overlap;

            if (entity != null && entity.Solid) {
                foreach (Entity other in entities) {
                    if (other != null && entity != other && other.Solid &&
                        (ret = !(overlap = entity.Collides(other.HitBox)).IsEmpty)) {
                        entity.Collision(other, overlap);
                        other.Collision(entity, overlap);

                        if (others != null) {
                            others.Add(other);
                        }
                    }
                }
            }

            return ret;
        }

    }
}
